"""
排班算法核心模块
功能：实现排班规则和人员选择逻辑
"""
from datetime import timedelta
import random


class Person:
    """人员类，存储单个人员信息和值班状态"""

    def __init__(self, person_id, name):
        self.id = person_id  # 人员ID（如工号）
        self.name = name  # 姓名
        self.unavailable_weeks = []  # 不可用周列表（如[3,5]表示第3,5周不值班）
        self.work_counts = 0  # 总值班次数统计
        self.last_shift_date = None  # 上次值班日期（用于避免连续值班）
        self.weekend_shifts = 0  # 周末值班次数（用于公平性控制）


class WorkPlan:
    """排班计划类，包含排班核心算法"""

    def __init__(self, start_date, end_date, people):
        self.start_date = start_date  # 排班开始日期
        self.end_date = end_date  # 排班结束日期
        self.people = people  # 参与排班的人员列表
        self.schedule = []  # 生成的排班表（字典列表）

    def _get_week_number(self, date):
        """计算当前是第几周（相对于开始日期）"""
        return (date - self.start_date).days // 7 + 1

    def generate(self):
        """生成排班表主方法"""
        current_date = self.start_date

        # 遍历每一天
        while current_date <= self.end_date:
            weekday = current_date.isoweekday()  # 获取星期几（1-7）
            is_weekend = weekday >= 6  # 判断是否是周末

            candidates = []
            for person in self.people:
                # 检查1：是否在不可用周
                if self._get_week_number(current_date) in person.unavailable_weeks:
                    continue

                # 检查2：是否连续值班
                if (person.last_shift_date and
                        (current_date - person.last_shift_date).days == 1):
                    continue

                # 检查3：是否连续周末值班
                if is_weekend:
                    last_weekend = current_date - timedelta(days=7)
                    if (person.last_shift_date and
                            person.last_shift_date == last_weekend):
                        continue

                candidates.append(person)  # 通过所有检查的候选人

            # 如果没有候选人，选择总值班最少的人（保底策略）
            if not candidates:
                candidates = [min(self.people, key=lambda x: x.work_counts)]

            # 选择策略：优先选值班次数最少的人
            selected = min(candidates, key=lambda x: x.work_counts)

            # 更新人员状态
            selected.work_counts += 1
            selected.last_shift_date = current_date
            if is_weekend:
                selected.weekend_shifts += 1

            # 记录排班结果
            self.schedule.append({
                "date": current_date,
                "weekday": weekday,
                "person": selected.name
            })

            current_date += timedelta(days=1)  # 移动到下一天

